import { TerrainConfig } from './config/MapConfig'; // Adjust path if needed
import { TerrainConfigItem, TerrainConfigManager, TerrainType } from './utils/TerrainConfigManager';
/**
* Represents a coordinate in the grid.
*/
interface Cell {
    row: number;
    col: number;
}
export class Terrain extends Laya.Box {
    private _terrain: Laya.Image;
    public id: number;
    public icon: string;
    public type: number;
    public img_skin: string;

    public anchorRow: number = 0;
    public anchorCol: number = 0;
    public occupiedGridSprites: any;
    public occupiedGrids: Array<{ row: number, col: number }> = [];

    // Base size in GRID UNITS. Initialize as undefined or null
    // to indicate they haven't been determined yet.
    baseGridWidth: number | undefined = undefined;
    baseGridHeight: number | undefined = undefined;
    public gridSizeCount: number | undefined = undefined;

    // Flag to track if size was explicitly set by config
    private sizeSetFromConfig: boolean = false;

    constructor() {
        super();
        // Basic setup
        this.anchorX = 0.5;
        this.anchorY = 1;
        this.mouseThrough = false;

        this._terrain = new Laya.Image();
        this._terrain.centerX = 0;
        this._terrain.bottom = 0;
        // this.bgColor = "#000000";
        this.addChild(this._terrain);
        // this.getGeneralizedTileCoordinates({ row: 5, col: 1 }, 2, 2)
    }

    /**
     * Initializes the terrain, loading configuration and image.
     * Returns a Promise that resolves when the terrain is fully initialized
     * (including having its base grid size determined, either from config or image).
     *
     * @param config Optional terrain configuration.
     * @param gridLogicWidth The logical width of a grid cell (e.g., 50). Needed for calculation.
     * @param gridRowSpacing The vertical distance between rows (e.g., 25). Needed for calculation.
     * @returns Promise<void>
     */
    public async initTerrain(
        config?: TerrainConfigItem,

    ): Promise<void> {
        return new Promise((resolve, reject) => {
            // 1. Apply basic config values
            if (config) {
                this.id = config.id;
                this.type = config.type;
                this.icon = config.icon;
                this.img_skin = config.icon;

                // Check if config provides explicit base size
                if (config.offset) {
                    this._terrain.centerX = config.offset[0];
                    this._terrain.bottom = config.offset[1];
                }

                if (config.size[0] !== undefined && config.size[1] !== undefined) {
                    this.baseGridWidth = config.size[0];
                    this.baseGridHeight = config.size[1];
                    let gridSizeCount = (this.baseGridWidth + this.baseGridHeight) / 2
                    const logicalBaseWidthPx = gridSizeCount * this.gridLogicWidth;
                    const logicalBaseHeightPx = gridSizeCount * this.gridRowSpacing;
                    this.size(logicalBaseWidthPx, logicalBaseHeightPx);
                    this.anchorX = this.baseGridWidth / 2 / gridSizeCount
                    this.gridSizeCount = gridSizeCount
                    this.sizeSetFromConfig = true; // Mark that size came from config
                    console.log(`Terrain ${this.id} initialized. Base size from config: ${this.baseGridWidth}x${this.baseGridHeight} grids.`);
                } else {
                    console.log(`Terrain ${this.id} initialized. Base size will be calculated from image.`);
                }
                // Config position might be less relevant now
            } else {
                // Default values if no config
                this.id = Date.now();
                this.type = 0;
                this.icon = "comp/Terrain/placeholder.png"; // Make sure this path is valid
                this.img_skin = this.icon;
                this.sizeSetFromConfig = false; // Size needs calculation
                console.log(`Terrain ${this.id} initialized with defaults. Base size will be calculated.`);
            }

            // 2. Set image skin and wait for load/error
            if (!this.img_skin) {
                console.error(`Terrain ${this.id} has no valid image skin path.`);
                this.img_skin = "comp/Terrain/error.png"; // Fallback error image
            }

            // Clear previous listeners before adding new ones
            this._terrain.offAll();
            this.checkTerrainSize()
            // Add LOADED listener
            this._terrain.once(Laya.Event.LOADED, this, () => {
                console.log(`Terrain ${this.id} image loaded: ${this._terrain.source}`);

                this.checkTerrainSize()
                if (this.type == TerrainType.NPC) {
                    let scale = 50 / this._terrain.displayWidth
                    this._terrain.scale(scale, scale)
                }

                // Optional: Adjust Box size after loading?
                // this.size(this._terrain.width, this._terrain.height);

                resolve(); // Initialization complete (including size determination)
            });

            // Add ERROR listener

            // 4. Initiate loading
            this._terrain.skin = TerrainConfigManager.getInstance().getFullResourcePath(this.img_skin); // Prepend resources path if needed

        }); // End of Promise constructor
    }
    gridLogicWidth: number = 50 // Provide default grid dimensions
    gridRowSpacing: number = 25
    checkTerrainSize() {
        // 3. Calculate base size IF NOT set by config
        if (!this.sizeSetFromConfig) {
            if (this._terrain.displayWidth > 0 && this.gridLogicWidth > 0 && this.gridRowSpacing > 0) {
                // Calculate baseGridWidth: Round image width up to nearest grid width multiple
                this.baseGridWidth = Math.ceil(this._terrain.displayWidth / this.gridLogicWidth);

                // Calculate logical base height (half of the calculated logical width)
                const logicalBaseWidthPx = this.baseGridWidth * this.gridLogicWidth;

                const logicalBaseHeightPx = logicalBaseWidthPx / 2;

                this.baseGridHeight = Math.ceil(logicalBaseHeightPx / this.gridRowSpacing);

                this.size(logicalBaseWidthPx, logicalBaseHeightPx);

                // Y轴向下为正
                const diamondPoints = [
                    0, -logicalBaseHeightPx / 2,    // Top vertex (中心点 Y - 半高)
                    logicalBaseWidthPx / 2, 0,              // Right vertex (中心点 X + 半宽)
                    0, logicalBaseHeightPx / 2,     // Bottom vertex (中心点 Y + 半高)
                    -logicalBaseWidthPx / 2, 0               // Left vertex (中心点 X - 半宽)
                ];

                // this.graphics.drawPoly(logicalBaseWidthPx / 2, logicalBaseHeightPx / 2, diamondPoints, '#000000', '#000000', 1)

                // Ensure minimum size is 1x1
                if (this.baseGridWidth < 1) this.baseGridWidth = 1;
                if (this.baseGridHeight < 1) this.baseGridHeight = 1;

                console.log(` -> Calculated Base Size for Terrain ${this.id}: ${this.baseGridWidth}x${this.baseGridHeight} grids (from image ${this._terrain.displayWidth}w)`);
            } else {
                console.warn(` -> Could not calculate base size for Terrain ${this.id}: Image width (${this._terrain.displayWidth}) or grid dimensions are invalid. Defaulting to 1x1.`);
                this.baseGridWidth = 1;
                this.baseGridHeight = 1;
            }
        }
        // Ensure size is never undefined after load
        if (this.baseGridWidth === undefined) this.baseGridWidth = 1;
        if (this.baseGridHeight === undefined) this.baseGridHeight = 1;

    }

    // getConfig remains the same
    public getConfig(): TerrainConfig {
        return {
            id: this.id,
            icon: this.icon,
            type: this.type,
            position: {
                x: this.anchorRow,
                y: this.anchorCol
            },
            baseGridWidth: this.baseGridWidth,
            baseGridHeight: this.baseGridHeight
        };
    }

    posGrid = { row: 0, col: 0 }
    /**
     * Calculates the position of the terrain in the grid based on its anchor position.
     *
     * @param anchorPos The anchor position of the terrain.

    /**
  * Calculates occupied grid cells based on the provided reference logic,
  * which seems to create a skewed parallelogram shape.
  * The calculation starts from the anchor cell and uses the baseGridWidth (W)
  * and baseGridHeight (H) as the dimensions for the skewed area.
  *
  * @param gridLogicWidth (Not directly used in this specific logic)
  * @param gridRowSpacing (Not directly used in this specific logic)
  * @param screenX The screen X coordinate of the terrain's anchor (bottom-center).
  * @param screenY The screen Y coordinate of the terrain's anchor (bottom-center).
  * @param mapGrid The 2D array representing the map state.
  * @param screenToGrid Function to convert screen coords to grid {row, col}.
  * @param gridToScreen Function to convert grid {row, col} to screen {x, y}.
  * @returns boolean - True if all calculated occupied grids are valid, False otherwise.
  */
    public updateOccupiedGrids(
        gridLogicWidth: number,
        gridRowSpacing: number,
        screenX: number,
        screenY: number,
        mapGrid: Array<Array<{ id: number, x: number, y: number, row: number, column: number, type: number }>>,
        screenToGrid: (x: number, y: number) => { row: number, col: number },
        gridToScreen: (row: number, col: number) => { x: number, y: number }
    ): boolean {
        this.occupiedGrids = [];
        let allGridsValid = true;

        // --- Crucial Check: Ensure base size is determined ---
        if (this.baseGridWidth === undefined || this.baseGridHeight === undefined || this.baseGridWidth < 1 || this.baseGridHeight < 1) {
            console.error(`Terrain ${this.id}: Cannot update occupied grids. Base size (W=${this.baseGridWidth}, H=${this.baseGridHeight}) is invalid or not determined.`);
            return false;
        }
        // --- End Check ---

        // 1. Find the grid cell where the bottom-center anchor lands
        // 计算锚点位置时，考虑地形的实际大小和格子的尺寸
        const anchorPos = screenToGrid(
            screenX, // 水平居中 300/2 150
            screenY  // 垂直居中
        )
        this.anchorRow = anchorPos.row; // Starting row 'r'
        this.anchorCol = anchorPos.col; // Starting column 'c'
        console.log(this.anchorRow, this.anchorCol)
        const W = this.baseGridWidth;  // Corresponds to setArea[0] in your reference
        const H = this.baseGridHeight; // Corresponds to setArea[1] in your reference
        this.occupiedGrids = this.generateCells({ row: this.anchorRow, col: this.anchorCol }, { width: W, height: H })
        console.log(this.occupiedGrids)
        for (let i = 0, len = this.occupiedGrids.length; i < len; i++) {
            let cell = this.occupiedGrids[i]
            if (cell.row < 0 || cell.row >= mapGrid.length ||
                !mapGrid[cell.row] ||
                cell.col < 0 || cell.col >= mapGrid[cell.row].length) {
                // Out of bounds
                if (allGridsValid) {
                    allGridsValid = false;
                    return allGridsValid
                }

            } else {
                // Check passability
                const gridType = mapGrid[cell.row][cell.col];
                if (gridType.type !== 0) { // Assuming 0 is passable
                    if (allGridsValid) {
                        /* console.warn(...) */
                        allGridsValid = false;
                        return allGridsValid
                    }

                }
            }
        }
        // // 2. Calculate the initial "spine" cells (vertical line upwards)
        // let spineCells: Array<{ c: number, r: number }>;
        // if (this.anchorRow % 2 === 0) { // Anchor row is Even
        //     spineCells = this._calculateSpineByEven(this.anchorCol, this.anchorRow, H, -1);
        // } else { // Anchor row is Odd
        //     spineCells = this._calculateSpineByOdd(this.anchorCol, this.anchorRow, H, -1);
        // }

        // // Use a Set to avoid duplicate grid cells if expansions overlap
        // const occupiedSet = new Set<string>();

        // // 3. For each cell in the spine, expand horizontally/downwards
        // for (const spineCell of spineCells) {
        //     let horizontalCells: Array<{ c: number, r: number }>;
        //     if (spineCell.r % 2 === 0) { // Spine cell row is Even
        //         // The reference used dire=1 for the horizontal part. Let's replicate.
        //         // Note: areaX is W (baseGridWidth)
        //         horizontalCells = this._calculateSpineByEven1(spineCell.c, spineCell.r, W, -1);
        //     } else { // Spine cell row is Odd
        //         horizontalCells = this._calculateSpineByOdd1(spineCell.c, spineCell.r, W, -1);
        //     }

        //     // 4. Add cells from horizontal expansion to the set and check validity
        //     for (const cell of horizontalCells) {
        //         const cellKey = `${cell.r},${cell.c}`; // Create unique key for the Set

        //         // Add to set only if it's not already there
        //         if (!occupiedSet.has(cellKey)) {
        //             occupiedSet.add(cellKey);
        //             this.occupiedGrids.push({ row: cell.r, col: cell.c }); // Add to final list

        //             // Check validity (bounds and passability)
        //             if (cell.r < 0 || cell.r >= mapGrid.length ||
        //                 !mapGrid[cell.r] ||
        //                 cell.c < 0 || cell.c >= mapGrid[cell.r].length) {
        //                 // Out of bounds
        //                 if (allGridsValid) { /* console.warn(...) */ }
        //                 allGridsValid = false;
        //             } else {
        //                 // Check passability
        //                 const gridType = mapGrid[cell.r][cell.c];
        //                 if (gridType.type !== 0) { // Assuming 0 is passable
        //                     if (allGridsValid) { /* console.warn(...) */ }
        //                     allGridsValid = false;
        //                 }
        //             }
        //         }
        //     } // End horizontal cell loop
        // } // End spine cell loop


        return allGridsValid;
    }


    /**
     * Calculates cells along a diagonal based on ODD starting row logic.
     * Replicates getMapInfoByOdd.
     * @param c Starting column
     * @param r Starting row
     * @param steps Number of steps (areaX / areaY)
     * @param dire Direction multiplier for row changes (-1 for up, 1 for down)
     * @returns Array of calculated {c, r} cells
     */

    private _calculateSpineByOdd(c: number, r: number, steps: number, dire: number = -1): Array<{ c: number, r: number }> {
        const arr = [];
        // Start with the initial cell itself? The reference loop starts from i=0.
        // Let's assume the reference includes the starting cell in the count 'steps'.
        for (let i = 0; i < steps; i++) {
            // Column offset increases every two steps
            const colOffset = -Math.floor(i / 2);
            // Row changes by i * dire relative to the STARTING row r
            const targetRow = r + i * dire;
            // Column changes by colOffset relative to the STARTING column c
            const targetCol = c + colOffset;
            arr.push({ c: targetCol, r: targetRow });
        }
        // If steps = 1, loop runs for i=0, colOffset=0, pushes {c, r}
        // If steps = 2, loop runs i=0 (pushes {c,r}), i=1 (colOffset=0, pushes {c, r+dire})
        // If steps = 3, loop runs i=0({c,r}), i=1({c,r+dire}), i=2(colOffset=1, pushes {c+1, r+2*dire})
        return arr;
    }
    private _calculateSpineByOdd1(c: number, r: number, steps: number, dire: number = -1): Array<{ c: number, r: number }> {
        const arr = [];
        // Start with the initial cell itself? The reference loop starts from i=0.
        // Let's assume the reference includes the starting cell in the count 'steps'.
        for (let i = 0; i < steps; i++) {
            // Column offset increases every two steps
            const colOffset = Math.floor(i / 2);
            // Row changes by i * dire relative to the STARTING row r
            const targetRow = r + i * dire;
            // Column changes by colOffset relative to the STARTING column c
            const targetCol = c + colOffset;
            arr.push({ c: targetCol, r: targetRow });
        }
        // If steps = 1, loop runs for i=0, colOffset=0, pushes {c, r}
        // If steps = 2, loop runs i=0 (pushes {c,r}), i=1 (colOffset=0, pushes {c, r+dire})
        // If steps = 3, loop runs i=0({c,r}), i=1({c,r+dire}), i=2(colOffset=1, pushes {c+1, r+2*dire})
        return arr;
    }

    /**
     * Calculates cells along a diagonal based on EVEN starting row logic.
     * Replicates getMapInfoByEven.
     * @param c Starting column
     * @param r Starting row
     * @param steps Number of steps (areaX / areaY)
     * @param dire Direction multiplier for row changes (-1 for up, 1 for down)
     * @returns Array of calculated {c, r} cells
     */
    private _calculateSpineByEven(c: number, r: number, steps: number, dire: number = -1): Array<{ c: number, r: number }> {
        const arr = [];
        for (let i = 0; i < steps; i++) {
            // Column offset uses Math.round
            const colOffset = -Math.round(i / 2);
            const targetRow = r + i * dire;
            const targetCol = c + colOffset;
            arr.push({ c: targetCol, r: targetRow });
        }
        // If steps = 1, i=0, colOffset=0, pushes {c, r}
        // If steps = 2, i=0({c,r}), i=1(colOffset=1, pushes {c+1, r+dire})
        // If steps = 3, i=0({c,r}), i=1({c+1,r+dire}), i=2(colOffset=1, pushes {c+1, r+2*dire})
        return arr;
    }


    private _calculateSpineByEven1(c: number, r: number, steps: number, dire: number = -1): Array<{ c: number, r: number }> {
        const arr = [];
        for (let i = 0; i < steps; i++) {
            // Column offset uses Math.round
            const colOffset = Math.round(i / 2);
            const targetRow = r + i * dire;
            const targetCol = c + colOffset;
            arr.push({ c: targetCol, r: targetRow });
        }
        // If steps = 1, i=0, colOffset=0, pushes {c, r}
        // If steps = 2, i=0({c,r}), i=1(colOffset=1, pushes {c+1, r+dire})
        // If steps = 3, i=0({c,r}), i=1({c+1,r+dire}), i=2(colOffset=1, pushes {c+1, r+2*dire})
        return arr;
    }
    generateCells(bottomCell: Cell, terrainSize: { width: number; height: number }): Cell[] {
        let { col: x, row: y } = bottomCell;
        const { width, height } = terrainSize;

        let cells: Cell[] = [{ row: y, col: x }]; // 先加入底部格子
        let leftCells: Cell[] = [{ row: y, col: x }]; // 存放往左上的格子
        let rightCells: Cell[] = []; // 存放往右上的格子

        // 计算往左上的 A 个格子
        let cx = x, cy = y;
        for (let i = 0; i < width - 1; i++) { // 左上移动 width-1 步
            if (cy % 2 === 0) { // 偶数行：列和行都减少
                cx -= 1;
            }
            cy -= 1;
            leftCells.push({ row: cy, col: cx });
        }

        console.log(leftCells); // 3x3 地形调试输出

        // 计算 M 中每个格子的右上方向格子
        for (const { row: ly, col: lx } of leftCells) {
            cx = lx;
            cy = ly;
            for (let i = 0; i < height - 1; i++) { // 右上移动 height-1 步
                if (cy % 2 === 0) { // 偶数行：仅行减少
                    cy -= 1;
                } else { // 奇数行：行减少，列增加
                    cx += 1;
                    cy -= 1;
                }
                rightCells.push({ col: cx, row: cy });
            }
        }

        // 合并所有格子并去重
        const uniqueCells = new Map<string, Cell>(); // 使用 Map 去重
        for (const cell of [...cells, ...leftCells, ...rightCells]) {
            uniqueCells.set(`${cell.row},${cell.col}`, cell);
        }

        return Array.from(uniqueCells.values());
    }



}